/*
 * Copyright 2016-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 严小杰最后修改于2023年10月17日
 */

package io.github.pnoker.driver.service.netty;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.pnoker.api.center.manager.RobIdRobNameApiGrpc;
import io.github.pnoker.api.center.manager.RobNameRequest;
import io.github.pnoker.api.center.manager.RobNameResponse;
import io.github.pnoker.common.constant.service.ManagerServiceConstant;
import io.github.pnoker.common.entity.driver.AttributeInfo;
import io.github.pnoker.common.entity.point.PointValue;
import io.github.pnoker.common.model.Device;
import io.github.pnoker.common.model.Point;
import io.github.pnoker.driver.sdk.DriverContext;
import io.github.pnoker.driver.sdk.service.DriverSenderService;
import io.github.pnoker.driver.sdk.utils.ConvertUtil;
import io.github.pnoker.driver.sdk.utils.DriverUtil;
import io.github.pnoker.driver.service.netty.tcp.NettyTcpServer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static io.github.pnoker.driver.service.netty.tcp.RobotXmlParser.extractAttribute;

/**
 * @author pnoker
 * @since 2022.1.0
 */
@Slf4j
@Service

public class NettyServerHandler {

    @Resource
    private DriverSenderService driverSenderService;

    @Resource
    private DriverContext driverContext;

    @GrpcClient(ManagerServiceConstant.SERVICE_NAME)
    private RobIdRobNameApiGrpc.RobIdRobNameApiBlockingStub robIdRobNameApiBlockingStub;


    public String getDeviceIdByName(String name) {
        List<Device> values = new ArrayList<>(driverContext.getDriverMetadata().getDeviceMap().values());
        for (int i = 0; i < values.size(); i++) {
            Device device = values.get(i);
            if (device.getDeviceName().equals(name)) {
                log.info("get DeviceID success!!!");
                return device.getId();
            }
        }
        return null;
    }

    public String getRobnameByRobID(String id, String craf){
        try {
            RobNameRequest.Builder builder = RobNameRequest.newBuilder().setRobid(id);
            RobNameResponse robNameResponse = robIdRobNameApiBlockingStub.selectByRobId(builder.build());
            String robname = robNameResponse.getRobname();
            log.info(robname+craf);
            return robname+"_"+craf;
        }catch (RuntimeException e){
            log.error("it looks like some grpc error getRobnameByRobID");
            log.error(e.toString());
            return null;
        }
    }


    public void read(ChannelHandlerContext context, ByteBuf byteBuf) {

        /* 展示收取到的16进制信息 */
        log.info("{}->{}", context.channel().remoteAddress(), ByteBufUtil.hexDump(byteBuf)); /* 打印获取机器人IP */
        String xml = byteBuf.toString(CharsetUtil.CHARSET_GBK); /* 先获取到机器人侧发送过来的原始字符串信息 */
        String id = extractAttribute(xml, "ID"); /* 获取机器人ID */
        String craf = extractAttribute(xml, "CRAF"); /* 获取设备类型名称，如，这里指SZ1，如果是其他设备，就要写其他设备的简称，如涂胶就是KL1，就是模板名称。 */
        String valueFromRob = extractAttribute(xml, "VALUE"); /*
                                                               * 获取参数值，如，这里指SZ1下的所有点位信号，key + 1111 1111 1111 1111 = 99
                                                               * 1111 1111 1111 1111。
                                                               */
        String deviceName = getRobnameByRobID(id, craf); /* 通过机器人发过来的 id 和 craf 获取设备名称 */
        log.info("devicename is  " + deviceName);
        String hexKey = valueFromRob.substring(0, 2); /* 通过机器人发过来的 value 获取 加密key */
        String values = valueFromRob.substring(2); /* 通过机器人发过来的 value 获取 需要分析的 values值 ，就是上述的 1111 1111 1111 1111 */
        /* 依据设备名称获取设备ID */
        String deviceId = getDeviceIdByName(deviceName); /* 通过 deviceName 获取deviceId */
        log.info("deviceId is : " + deviceId);

        // TODO 用于存储channel，然后配合write接口实现向下发送数据
        NettyTcpServer.deviceChannelMap.put(deviceId, context.channel());
        List<PointValue> pointValues = new ArrayList<>(16);
        /* 获取设备下的 所有 点位信息 */
        Map<String, Map<String, AttributeInfo>> pointInfoMap = driverContext.getDriverMetadata().getPointInfoMap()
                .get(deviceId);
        /* 遍历 点位地图pointInfoMap 获取设备下的所有点位信息 */
        if (pointInfoMap != null) {
            for (String pointId : pointInfoMap.keySet()) {

                /* 获取 单个点位point 的所有数据 */
                Point point = driverContext.getPointByDeviceIdAndPointId(deviceId, pointId);
                /* 获取 单个点位point 的所有数据 */
                Map<String, AttributeInfo> infoMap = pointInfoMap.get(pointId);
                int start = DriverUtil.value(infoMap.get("start").getType().getCode(), infoMap.get("start").getValue());
                int end = DriverUtil.value(infoMap.get("end").getType().getCode(), infoMap.get("end").getValue());
//                log.error(point.getPointName());
                /* 验证密钥通过后，更新存储数据。 即 发过的数据过来的的key 是否和 前端web一开始设定的key 一致，如一致，则允许进行数据更新 */
                if (infoMap.get("key").getValue().equals(hexKey)) {
                    PointValue pointValue = null;

                    /* 焊钳逻辑 */
                    /* 获取字符串里面第几位 */
                    boolean errorCode = Integer.parseInt(values.substring(start - 1, end)) > 0;

                    pointValue = new PointValue(deviceId, pointId, String.valueOf(errorCode),
                            ConvertUtil.convertValue(point, String.valueOf(errorCode)));
                    /* 焊钳逻辑 */

                    if (ObjectUtil.isNotNull(pointValue)) {
                        pointValues.add(pointValue);
                    }
                }
            }
            driverSenderService.pointValueSender(pointValues);
        } else {
            log.error("*** pointInfoMap is empty !!! check your point design in website !!!  ***");
        }
    }

}
